package fr.labri.macchiato.framework.business.sparql;

import java.util.Collections;
import java.util.Comparator;
import java.util.List;

import com.google.gwt.core.client.GWT;
import com.google.gwt.user.client.rpc.AsyncCallback;

import fr.labri.macchiato.framework.business.encoder.Encoders;
import fr.labri.macchiato.framework.business.finder.Callback;
import fr.labri.macchiato.framework.business.finder.ProductFinder;
import fr.labri.macchiato.framework.business.model.businessEntity.BusinessEntity;
import fr.labri.macchiato.framework.business.model.preferences.PreferenceKind;
import fr.labri.macchiato.framework.business.model.preferences.UserPreferences;
import fr.labri.macchiato.framework.business.model.product.Model;
import fr.labri.macchiato.framework.business.model.product.Offer;
import fr.labri.macchiato.framework.business.model.product.Product;
import fr.labri.macchiato.framework.technical.encoder.Encoder;
import fr.labri.macchiato.framework.technical.queryLauncher.RpcQueryLauncher;
import fr.labri.macchiato.framework.technical.queryLauncher.RpcQueryLauncherAsync;
import fr.labri.macchiato.framework.util.FrameworkException;

/**
 * A Class that permits to choose the best offer of a product by checking user's
 * preferences order and using SparQl request
 * 
 * @author Abdoulaye DIALLO
 * 
 * 
 */
public class SparqlProductFinder implements ProductFinder {

	private RpcQueryLauncherAsync queryLauncher;

	private QueryFactory getQueryFactory() {
		return QueryFactory.getInstance();
	}

	/**
	 * Default Constructor
	 */
	public SparqlProductFinder() {
		queryLauncher = GWT.create(RpcQueryLauncher.class);
	}

	@Override
	public void getBestOffer(final Product product,
			final UserPreferences userPreferences,
			final Callback<Product> callback) {

		findCorrespondingOffers(product, userPreferences,
				new Callback<List<Offer>>() {

					@Override
					public void onCallback(List<Offer> result) {

						Product bestOffer = product;
						List<PreferenceKind> prefs = userPreferences.getPreferencesOrder();
						PreferenceKind preferenceKind = prefs.get(0);
						switch (preferenceKind) {

						case BUSINESS_ENTITY:
							// TODO: Implement: find the best provider
							break;
						case DISTANCE:
							// TODO : find the closest provider
							break;
						case PRICE:
							// the corresponding offers are already sorted in
							// ascending order
							// so we just have to take the first element
							if (!result.isEmpty()) {
								bestOffer = result.get(0);
								bestOffer.setQuantity(product.getQuantity());
							}

							break;
						default:
							break;
						}
						callback.onCallback(bestOffer);

					}
				});

	}

	/**
	 * Find all the corresponding offers of the Product passed as parameter. The
	 * research will be based on the UserPreferences. Actually we don't care
	 * about the complicated cases.
	 */
	private void findCorrespondingOffers(Product product,
			UserPreferences userPreferences,
			final Callback<List<Offer>> callback) {

		String searchKey = product.getName();

		String searchQuery = QueryFactory.getInstance().getCheaperOfferQuery(
				searchKey);

		queryLauncher.launchQueryForJSON(searchQuery,
				new AsyncCallback<String>() {

					@Override
					public void onSuccess(String result) {

						List<Offer> offers = getOfferEncoder()
								.decodeSparqlResult(result);
						callback.onCallback(offers);
					}

					@Override
					public void onFailure(Throwable caught) {
						caught.printStackTrace();
						throw new FrameworkException(
								"Échec lors de la recherche");
					}
				});
	}

	/**
	 * Sorts a list of offers according to the user preferences, and minimizing
	 * either the price, or the distance to the shop
	 * 
	 * @param offers
	 * @param userPreferences
	 */
	@Override
	public void sortOffers(List<Offer> offers, UserPreferences userPreferences,
			PreferenceKind preferenceKind) {
		if (preferenceKind.equals(PreferenceKind.PRICE)) {			
			Collections.sort(offers, new Comparator<Offer>() {

				@Override
				public int compare(Offer o1, Offer o2) {	
					return Double.compare(o1.getPrice(),o2.getPrice());
				}
			});
		}
	}


	@Override
	public void getModels(String keyword, final Callback<List<Model>> callback) {
		String query = getQueryFactory().searchModels(keyword);
		queryLauncher.launchQueryForJSON(query, new AsyncCallback<String>() {

			@Override
			public void onSuccess(String result) {

				List<Model> models = getModelEncoder().decodeSparqlResult(
						result);
				callback.onCallback(models);
			}

			@Override
			public void onFailure(Throwable caught) {
				callback.onFailure(caught);
			}
		});
	}

	@Override
	public void getOffers(String keyword, final Callback<List<Offer>> callback) {
		String query = getQueryFactory().getOffersQuery(keyword);
		queryLauncher.launchQueryForJSON(query, new AsyncCallback<String>() {

			@Override
			public void onSuccess(String result) {

				List<Offer> offers = getOfferEncoder().decodeSparqlResult(
						result);
				callback.onCallback(offers);
			}

			@Override
			public void onFailure(Throwable caught) {
				callback.onFailure(caught);
			}
		});

	}
	
	@Override
	public void getOffers(String keyword, List<BusinessEntity> providers, final Callback<List<Offer>> callback) {
		String query = getQueryFactory().getOffersQuery(keyword, providers);
		queryLauncher.launchQueryForJSON(query, new AsyncCallback<String>() {

			@Override
			public void onSuccess(String result) {

				List<Offer> offers = getOfferEncoder().decodeSparqlResult(
						result);
				callback.onCallback(offers);
			}

			@Override
			public void onFailure(Throwable caught) {
				callback.onFailure(caught);
			}
		});

	}

	@Override
	public void getOffersByEan(String ean, final Callback<List<Offer>> callback) {
		String query = getQueryFactory().getOffersByEanQuery(ean);

		queryLauncher.launchQueryForJSON(query, new AsyncCallback<String>() {

			@Override
			public void onSuccess(String result) {

				List<Offer> offers = getOfferEncoder().decodeSparqlResult(
						result);
				callback.onCallback(offers);
			}

			@Override
			public void onFailure(Throwable caught) {
				callback.onFailure(caught);
			}
		});

	}
	
	@Override
	public void getOffersByEan(String ean, List<BusinessEntity> providers, final Callback<List<Offer>> callback) {
		String query = getQueryFactory().getOffersByEanQuery(ean, providers);
		System.out.println(query);
		queryLauncher.launchQueryForJSON(query, new AsyncCallback<String>() {

			@Override
			public void onSuccess(String result) {

				List<Offer> offers = getOfferEncoder().decodeSparqlResult(
						result);
				callback.onCallback(offers);
			}

			@Override
			public void onFailure(Throwable caught) {
				callback.onFailure(caught);
			}
		});

	}

	private Encoder<Offer> getOfferEncoder() {
		return Encoders.getInstance().getOfferEncoder();
	}

	private Encoder<Model> getModelEncoder() {
		return Encoders.getInstance().getModelEncoder();
	}

}
